package com.study.thread.juc.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 * Condition：（await与signal/signalAll）使用await();方法前是需要先获取lock这把锁的，调用await后在等待之前会释放这把锁，调用signal前要获得这把锁！
 *          await()：当调用此方式时需要已经获取到锁，否则会抛出异常，IllegalMonitorStateException
 *                   调用后会将当前线程等待，并释放掉锁，等待有人调用signal/signalALl方法或interrupt中断，线程会被唤醒，重新获取锁运行，返回一个true/false；
 *          await(long time, TimeUnit unit)：当调用此方式时需要已经获取到锁，否则会抛出异常，IllegalMonitorStateException
 *                    调用后传入对应的时间，线程会进入等待状态，并释放掉锁，等待当前时间到了或者有线程调用signal/signalALl方法或调用当前线程interrupt中断，
 *                    线程会被唤醒，重新获取锁运行，返回一个true/false；
 *          awaitNanos(long nanosTimeout)：当调用此方式时需要已经获取到锁，否则会抛出异常，IllegalMonitorStateException
 *                    调用时传入纳秒时间，线程会进入等待状态，直到指定时间过去了或者有线程调用了signal/singnalAll方法或者当前线程被interrupt中断了,
 *                    调用后会返回一个近似的未等待的纳秒时间，看当前等待了多久，假如给了500纳秒，等待了200纳秒，预算估计返回300纳秒，否则超时返回一个小于或等于0的数字
 *                    因为底层某些情况会有误差，所以采用的纳秒，可能会误差几纳秒所以没有用毫秒更大的来减少误差
 *          awaitUninterruptibly()：当调用此方式时需要已经获取到锁，否则会抛出异常，IllegalMonitorStateException
 *                    调用后会将当前线程等待，并释放掉锁，等待有人使用signal/signalAll方法来唤醒，此方法不受interrupt中断限制！
 *          signal()：调用此方式时需要已经获取到锁，否则会抛出异常，IllegalMonitorStateException
 *                    调用后会唤醒一个处于等待的线程，唤醒第一个等待的线程也就是头节点线程，此时这个线程被唤醒接着需要争抢到锁才可以继续执行等待前的剩余代码！
 *          signalAll()：调用此方式时需要已经获取到锁，否则会抛出异常，IllegalMonitorStateException
 *                    调用后会唤醒所有处于等待的线程，此时处于等待的线程被唤醒接着需要争抢到锁才可以继续执行等待前的剩余代码！
 *
 *  1、创建：通过Lock接口的newCondition(); 方法来创建Contiditon等待集合对象
 *     1.1：Condition可以创建多个等待集合，也就是说可以根据自己操作创建多个，使用任意一个Lock对象，就可以将它们组合起来，synchronized使用wait是单独的一个等待集合
 *  2、虚假唤醒：当使用signal/signalAll可能会包含线程不满足条件的，此时需要用while来检查条件是否满足，如果使用if则会接着往下执行
 *
 */
public class ConditionDemo {
    private static Lock lock = new ReentrantLock();
    private static Condition conProvider = lock.newCondition();
    private static Condition conConsumer = lock.newCondition();
    private static int count = 0;

    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                try {
                    lock.lock();
                    while (count > 0) {
                        try {
                            System.out.println("阻塞生产：");
                            conProvider.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    count++;
                    System.out.println(Thread.currentThread().getName() + "执行生产：" + count);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    conConsumer.signal();
                } finally {
                    lock.unlock();
                }
            }
        }, "生产者A").start();

        new Thread(() -> {
            while (true) {
                try {
                    lock.lock();
                    while (count > 0) {
                        try {
                            System.out.println("阻塞生产：");
                            conProvider.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    count++;
                    System.out.println(Thread.currentThread().getName() + "执行生产：" + count);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    conConsumer.signal();
                } finally {
                    lock.unlock();
                }
            }
        }, "生产者B").start();

        new Thread(() -> {
            while (true) {
                try {
                    lock.lock();
                    while (count <= 0) {
                        try {
                            System.out.println("阻塞消费：");
                            conConsumer.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "执行消费：" + count);

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    conProvider.signal();
                } finally {
                    lock.unlock();
                }
            }
        }, "消费者A").start();

        new Thread(() -> {
            while (true) {
                try {
                    lock.lock();
                    while (count <= 0) {
                        try {
                            System.out.println("阻塞消费：");
                            conConsumer.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "执行消费：" + count);

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    conProvider.signal();
                } finally {
                    lock.unlock();
                }
            }
        }, "消费者B").start();
    }
}
